package scm.inventoryControl.session.stateless;

import crm.salesOrderProcessing.entity.DeliveryOrderEntity;
import crm.salesOrderProcessing.entity.ProductEntity;
import crm.salesOrderProcessing.entity.SalesOrderEntity;
import java.util.ArrayList;
import scm.inventoryControl.entity.ProductBatchRecordEntity;
import scm.inventoryControl.entity.ProductInventoryLevelEntity;
import java.util.List;
import javax.ejb.Stateless;
import javax.ejb.LocalBean;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import scm.inventoryControl.entity.PackingOrderEntity;
import scm.inventoryControl.entity.PickingOrderEntity;
import scm.inventoryControl.entity.RMInventoryLevelEntity;
import scm.inventoryControl.entity.RawMaterialBatchRecordEntity;
import scm.inventoryControl.entity.ShippingOrderEntity;
import scm.inventoryControl.entity.UnfulfillOrderEntity;
import scm.rawMaterialPurchasing.entity.RawMaterialEntity;
import scm.rawMaterialPurchasing.entity.RawMaterialPurchasingOrderEntity;
import scm.rawMaterialPurchasing.entity.RawMaterialPurchasingOrderLineItemEntity;

@Stateless
@LocalBean
public class InventoryControlSessionBean {

    @PersistenceContext
    private EntityManager em;
    private ProductBatchRecordEntity newProductBatchRecord, oriProductBatchRecord;
    private ProductInventoryLevelEntity productInventoryLevel;
    private UnfulfillOrderEntity totalOweQty;
    private List<ProductBatchRecordEntity> records;
    private List<ProductBatchRecordEntity> targetedRecords;

    public List<DeliveryOrderEntity> getDeliveryOrders() {
        List<DeliveryOrderEntity> tempDeliveryOrder=new ArrayList<DeliveryOrderEntity>();
        List<DeliveryOrderEntity> tempDeliveryOrder2=new ArrayList<DeliveryOrderEntity>();
        Query query = em.createQuery("SELECT d FROM DeliveryOrderEntity d WHERE d.parentSalesOrder.statusId = 600" );
        tempDeliveryOrder=query.getResultList();
        for(int i=0;i<tempDeliveryOrder.size();i++){
            if(tempDeliveryOrder.get(i).isIsCIAPayment()==true && tempDeliveryOrder.get(i).isPaymentStatus()==false ){}
            else tempDeliveryOrder2.add(tempDeliveryOrder.get(i));
        }
        return tempDeliveryOrder2;
    }
    
    public void mergeLineItemBack(RawMaterialPurchasingOrderLineItemEntity lineItem){
    em.merge(lineItem);
    }
    
    public List<RawMaterialPurchasingOrderLineItemEntity> getUnReceivedLineItems(){
        Query query = em.createQuery("SELECT p FROM RawMaterialPurchasingOrderLineItemEntity p WHERE p.received=:inR");
          query.setParameter("inR", false);
       return (List<RawMaterialPurchasingOrderLineItemEntity>)query.getResultList();
    }
    
    
    //zan shi bu yong
    public List<RawMaterialPurchasingOrderLineItemEntity> getAllPOLineItems(){
        boolean hasUnreceived = false;
        Query query = em.createQuery("SELECT p FROM RawMaterialPurchasingOrderEntity p");
        List<RawMaterialPurchasingOrderEntity> purchasingOrder=query.getResultList();
        List<RawMaterialPurchasingOrderEntity> unReceivedPurchasingOrder=new ArrayList<RawMaterialPurchasingOrderEntity>();
        List<RawMaterialPurchasingOrderLineItemEntity> lineItemList=new ArrayList<RawMaterialPurchasingOrderLineItemEntity>();
        for(int i=0;i<purchasingOrder.size();i++){
            for(int j=0;j<purchasingOrder.get(i).getRawMaterialPurchasingOrderLineItems().size();j++){
                if(purchasingOrder.get(i).getRawMaterialPurchasingOrderLineItems().get(j).isReceived()==false){
                    hasUnreceived=true;
                }
             }
             if(hasUnreceived==true){
                    unReceivedPurchasingOrder.add(purchasingOrder.get(i));
             }
        }//end for
        for(int m=0;m<unReceivedPurchasingOrder.size();m++){
            for(int n=0;n<unReceivedPurchasingOrder.get(m).getRawMaterialPurchasingOrderLineItems().size();n++){
                lineItemList.add(unReceivedPurchasingOrder.get(m).getRawMaterialPurchasingOrderLineItems().get(n));
            }
        }
        
        return lineItemList;
        
    }

    public void saveEdit(ProductInventoryLevelEntity proInventory) {
        em.merge(proInventory);
    }
    public void saveChangesOnRMInventory(RMInventoryLevelEntity rmInventoryLevelEntity){
        em.merge(rmInventoryLevelEntity);
    }

    public void deleteRecord(ProductBatchRecordEntity productBatch) {
        ProductBatchRecordEntity temp = em.find(ProductBatchRecordEntity.class, productBatch.getId());
        temp.getProductInventoryLevel().setCurrentLevel(temp.getProductInventoryLevel().getCurrentLevel() - temp.getQuantity());
        ProductInventoryLevelEntity inventory = temp.getProductInventoryLevel();
        //em.merge(inventory);        
        em.remove(temp);
        em.merge(inventory);
        em.flush();
    }

    public List<ProductInventoryLevelEntity> getAllProductInventories() {
        Query query = em.createQuery("SELECT sl FROM ProductInventoryLevelEntity sl");
        return query.getResultList();
    }

    public List<RMInventoryLevelEntity> getAllRawMaterialInventories() {
        Query query = em.createQuery("SELECT rl FROM RMInventoryLevelEntity rl");
        return query.getResultList();
    }
    
    public List<RawMaterialEntity> getAllRawMaterials(){
           Query query = em.createQuery("SELECT rl FROM RawMaterialEntity rl");
        return query.getResultList();
    }

    public List<ProductBatchRecordEntity> getAllProductRecords() {
        Query query = em.createQuery("SELECT sl FROM ProductBatchRecordEntity sl");
        return query.getResultList();
    }

    public List<RawMaterialBatchRecordEntity> getAllRawMaterialRecords() {
        Query query = em.createQuery("SELECT sl FROM RawMaterialBatchRecordEntity sl");
        return query.getResultList();
    }

    public UnfulfillOrderEntity getTotalOweQuantityByProductName(String productName) {
         Query query = em.createQuery("SELECT r FROM ProductEntity r WHERE r.productName = :inName");
        query.setParameter("inName", productName);
        
        ProductEntity product = (ProductEntity) query.getSingleResult();
        
         query = em.createQuery("SELECT n FROM UnfulfillOrderEntity n WHERE n.product = :Inproduct");
        query.setParameter("Inproduct", product);
        return (UnfulfillOrderEntity) query.getSingleResult();
    }

    public void reserveProduct(SalesOrderEntity salesOrder) {
        for (int i = 0; i < salesOrder.getSalesOrderLineItems().size(); i++) {
            int requiredQty = salesOrder.getSalesOrderLineItems().get(i).getQuantity();

            String productName = salesOrder.getSalesOrderLineItems().get(i).getProduct().getProductName();
            UnfulfillOrderEntity totalQweQty = getTotalOweQuantityByProductName(productName);
            int avaliableInventory = getCurrentInventoryLevel(productName);
            if (requiredQty <= avaliableInventory) {
                //current inventory can fulfill the salesOrder
                reserveProductForEachLineItem(salesOrder, productName, requiredQty);
            } else {//current inventory cannot fulfill the salesOrder
                if (avaliableInventory > 0) {
                    reserveProductForEachLineItem(salesOrder, productName, avaliableInventory);
                    totalQweQty.setTotalOweQuantity(requiredQty - avaliableInventory);
                } else { //avaliableInventory=0
                    totalQweQty.setTotalOweQuantity(requiredQty);
                }
                em.merge(totalQweQty);
            }
        }
    }

    //for each product
    private void reserveProductForEachLineItem(SalesOrderEntity salesOrder, String productName, int requiredQuantity) throws NoResultException {//reserve product based on FIFO of product record
        Query query = em.createQuery("SELECT n FROM ProductBatchRecordEntity n WHERE n.productInventoryLevel = :inInventoryLevel Order by n.manufactureDate ");
        query.setParameter("inInventoryLevel", getInventoryByProductName(productName));
        int tempQuantity = requiredQuantity;
        setRecords((List<ProductBatchRecordEntity>) query.getResultList());
        int i = 0;
        while (tempQuantity > 0 && i < getRecords().size()) {
            oriProductBatchRecord = getRecords().get(i);
            if (oriProductBatchRecord.getQuantity() > tempQuantity) { //the record can fulfill the order
                newProductBatchRecord = new ProductBatchRecordEntity();
                newProductBatchRecord.setQuantity(oriProductBatchRecord.getQuantity() - tempQuantity);

                oriProductBatchRecord.setQuantity(tempQuantity);
                //oriProductBatchRecord.setSalesOrder(salesOrder);
                oriProductBatchRecord.setReserved(true);
                em.merge(oriProductBatchRecord);

                newProductBatchRecord.setManufactureDate(oriProductBatchRecord.getManufactureDate());
                newProductBatchRecord.setProductInventoryLevel(oriProductBatchRecord.getProductInventoryLevel());
                newProductBatchRecord.setWarehouseLocation(oriProductBatchRecord.getWarehouseLocation());

                em.persist(newProductBatchRecord);
            }//end of if   
            else {
                oriProductBatchRecord.setReserved(true);
                //oriProductBatchRecord.setSalesOrder(salesOrder);
                em.merge(oriProductBatchRecord);
            }
            tempQuantity = tempQuantity - oriProductBatchRecord.getQuantity();
            i++;
        }//end of while
   //update inventory level->increase reserved inventory
        ProductInventoryLevelEntity productInventory = getInventoryByProductName(productName);
        productInventory.setReservedQty(getInventoryByProductName(productName).getReservedQty() + requiredQuantity);
        //update inventory level->decrese current inventory
        productInventory.setCurrentLevel(getInventoryByProductName(productName).getCurrentLevel() - requiredQuantity);
        em.merge(productInventory);
    }
  
    public void saveOriNewRecords(ProductBatchRecordEntity oriRecord,ProductBatchRecordEntity newRecord){
        em.persist(newRecord);
        em.merge(oriRecord);
        em.flush();
                
    }
    


    public int getCurrentInventoryLevel(String productName) {
        return getInventoryByProductName(productName).getCurrentLevel();
    }

    public ProductInventoryLevelEntity getInventoryByProductName(String productName) {
        //get product by name first
        Query query = em.createQuery("SELECT u FROM ProductEntity u WHERE u.productName = :inName");
        query.setParameter("inName", productName);
        ProductEntity product = null;
        product = (ProductEntity) query.getSingleResult();
        //get inventory by product
        Query query2 = em.createQuery("SELECT m FROM ProductInventoryLevelEntity m WHERE m.product = :inProduct");
        query2.setParameter("inProduct", product);
        return (ProductInventoryLevelEntity) query2.getSingleResult();
    }

    public RMInventoryLevelEntity getInventoryByRMName(String name) {
        Query query = em.createQuery("SELECT r FROM RawMaterialEntity r WHERE r.rawMaterialName = :inName");
        query.setParameter("inName", name);
        RawMaterialEntity rawMaterial = null;
        rawMaterial = (RawMaterialEntity) query.getSingleResult();
        //get inventory by rawmaterial
        Query query2 = em.createQuery("SELECT m FROM RMInventoryLevelEntity m WHERE m.rawMaterial = :inRM");
        query2.setParameter("inRM", rawMaterial);
        return (RMInventoryLevelEntity) query2.getSingleResult();
    }

    public void saveProductRecord(ProductBatchRecordEntity productBatchRecord) {
        em.persist(productBatchRecord);
    }

    public void saveRMRecord(RawMaterialBatchRecordEntity rmBatchRecord) {
        em.persist(rmBatchRecord);
    }

    public void saveDeliveryRelationship(DeliveryOrderEntity selectedDeliveryOrder) {
        em.merge(selectedDeliveryOrder);
        em.flush();
    }

    public void adjustRMInventoryLevel(RawMaterialBatchRecordEntity rmBatchRecord) {
        RawMaterialBatchRecordEntity rmBatchRecordEntity = em.find(RawMaterialBatchRecordEntity.class, rmBatchRecord.getId());

        rmBatchRecordEntity.getRmInventoryLevel().setCurrentLevel(rmBatchRecordEntity.getRmInventoryLevel().getCurrentLevel() + rmBatchRecordEntity.getQuantity());
        //rmBatchRecordEntity.getRmInventoryLevel().setIncomingQty(rmBatchRecordEntity.getRmInventoryLevel().getIncomingQty() - rmBatchRecordEntity.getQuantity());
        em.merge(rmBatchRecordEntity.getRmInventoryLevel());
        em.flush();
    }
  //for normal finalgoods record: decrease incoming, increase current
    public void adjustInventoryLevel(ProductBatchRecordEntity productBatchRecord) {
        ProductBatchRecordEntity productBatchRecordEntity = em.find(ProductBatchRecordEntity.class, productBatchRecord.getId());
        productBatchRecordEntity.getProductInventoryLevel().setCurrentLevel(productBatchRecordEntity.getProductInventoryLevel().getCurrentLevel() + productBatchRecordEntity.getQuantity());
       // productBatchRecordEntity.getProductInventoryLevel().setIncomingQty(productBatchRecordEntity.getProductInventoryLevel().getIncomingQty() - productBatchRecordEntity.getQuantity());
        em.merge(productBatchRecordEntity.getProductInventoryLevel());
        em.flush();
    }
//for reserve in advance finalgoods record: decrease incoming, increase reserved
    public void adjustInventoryLevel2(ProductBatchRecordEntity productBatchRecord){
        ProductBatchRecordEntity productBatchRecordEntity = em.find(ProductBatchRecordEntity.class, productBatchRecord.getId());
        productBatchRecordEntity.getProductInventoryLevel().setReservedQty(productBatchRecordEntity.getProductInventoryLevel().getReservedQty() + productBatchRecordEntity.getQuantity());
        //productBatchRecordEntity.getProductInventoryLevel().setIncomingQty(productBatchRecordEntity.getProductInventoryLevel().getIncomingQty() - productBatchRecordEntity.getQuantity());
        em.merge(productBatchRecordEntity.getProductInventoryLevel());
        em.flush();
    }
    
    public void adjustInventoryLevel3(ProductBatchRecordEntity oriR,ProductBatchRecordEntity newR){
       
        oriR.getProductInventoryLevel().setReservedQty(oriR.getProductInventoryLevel().getReservedQty() + oriR.getQuantity());
        //oriR.getProductInventoryLevel().setIncomingQty(oriR.getProductInventoryLevel().getIncomingQty() - oriR.getQuantity()-newR.getQuantity());
        oriR.getProductInventoryLevel().setCurrentLevel(oriR.getProductInventoryLevel().getCurrentLevel()+ newR.getQuantity());
       
        em.merge(oriR.getProductInventoryLevel());
        em.flush();
    }
    
    public void adjustTotalOweQuantity(UnfulfillOrderEntity totalOweQuantity,ProductBatchRecordEntity record){
         UnfulfillOrderEntity totalOweQuantityEntity = em.find(UnfulfillOrderEntity.class, totalOweQuantity.getId());
          totalOweQuantityEntity.setTotalOweQuantity(totalOweQuantity.getTotalOweQuantity()-record.getQuantity());
         
         em.merge(totalOweQuantityEntity );
         em.flush();
    }
    
    public void adjustTotalOweQuantity2(UnfulfillOrderEntity totalOweQuantity){
    UnfulfillOrderEntity totalOweQuantityEntity = em.find(UnfulfillOrderEntity.class, totalOweQuantity.getId());
          totalOweQuantityEntity.setTotalOweQuantity(0);
          em.merge(totalOweQuantityEntity);
          em.flush();
    }
         
    
    public void savePickingOrder(PickingOrderEntity pickingOrder) {
        em.persist(pickingOrder);
    }

    public void savePackingOrder(PackingOrderEntity packingOrder) {
        em.persist(packingOrder);
    }

    public void saveShippingOrder(ShippingOrderEntity shippingOrder) {
        em.persist(shippingOrder);
    }

    public ProductBatchRecordEntity getProductBatchRecord() {
        return newProductBatchRecord;
    }

    public void setProductBatchRecord(ProductBatchRecordEntity productBatchRecord) {
        this.newProductBatchRecord = productBatchRecord;
    }

    public ProductInventoryLevelEntity getProductInventoryLevel() {
        return productInventoryLevel;
    }

    public void setProductInventoryLevel(ProductInventoryLevelEntity productInventoryLevel) {
        this.productInventoryLevel = productInventoryLevel;
    }

    public List<ProductBatchRecordEntity> getRecords() {
        return records;
    }

    public void setRecords(List<ProductBatchRecordEntity> records) {
        this.records = records;
    }

    public List<ProductBatchRecordEntity> getTargetedRecords() {
        return targetedRecords;
    }

    public void setTargetedRecords(List<ProductBatchRecordEntity> targetedRecords) {
        this.targetedRecords = targetedRecords;
    }

    public ProductBatchRecordEntity getOriProductBatchRecord() {
        return oriProductBatchRecord;
    }

    public void setOriProductBatchRecord(ProductBatchRecordEntity oriProductBatchRecord) {
        this.oriProductBatchRecord = oriProductBatchRecord;
    }

    public UnfulfillOrderEntity getTotalOweQty() {
        return totalOweQty;
    }

    public void setTotalOweQty(UnfulfillOrderEntity totalOweQty) {
        this.totalOweQty = totalOweQty;
    }
}
